#include "Player/PlayerData.h"
#include "Log.h"
#include "ServerConfigure.h"
#include "Managers/SkinsManager.h"
#include "Network/MessageManager.h"
#include "Subsystems/SubsystemTerrain.h"
#include "Subsystems/SubsystemGameInfo.h"
#include "Subsystems/SubsystemTimeOfDay.h"

#include "Network/Packages/SubsystemTerrainPackage.h"
#include "Network/PackageManager.h"

#include "GameRandom.hpp"
#include "GameSingleton.h"

namespace PocketSurvival
{
    PlayerData::PlayerData(ClientPtr theClient) : 
        client(theClient),
        terrainStatue(LoadTerrainStatue::NO_LOADING),
        playerClass(PlayerClass::Male),
        entity(entt::null),
        level(1),
        firstSpawnTime(-1),
        lastSpawnTime(-1),
        spawnPosition(0,-1,0),
        spawnsCount(0),
        m_machine(nullptr),
        m_inServerTime(0),
        m_loadTerrainCompiletedCount(0) {}

    PlayerData::~PlayerData()
    {
        if(m_machine != nullptr)
        {
            delete m_machine;
            m_machine = nullptr;
        }
    }

    void PlayerData::update(float dt)
    {
        m_inServerTime += dt;
        m_machine->update(dt);
    }

    void PlayerData::updateRequestChunk()
    {
        SubsystemTerrain *subsystemTerrain = GameSingleton::singletonPtr->getSingleton<SubsystemTerrain>();

        std::lock_guard<std::mutex> lk(m_requestChunkQueueMutex);
    
        if (m_requestChunkQueue.size() > 0)
        {
            terrainStatue = LoadTerrainStatue::LOADING;
        }
        else if (terrainStatue == LoadTerrainStatue::LOADING)
        {
            terrainStatue = LoadTerrainStatue::LOADING_COMPLETED;
            m_loadTerrainCompiletedCount++;
        }
        
        while(m_requestChunkQueue.size() > 0)
        {
            if(m_requestChunkQueue.size() >= ((GameSingleton::serverConfigure->viewChunkCount + GameSingleton::serverConfigure->viewChunkCount / 4) * 20))
            {
                std::queue<Point2> newQueue;
                m_requestChunkQueue.swap(newQueue);
                client->disconnect(fmt::format("你的视野范围过大，视野范围必须是 {} 以内", GameSingleton::serverConfigure->viewChunkCount * 16));
                return;
            }
            Point2 &point = m_requestChunkQueue.front();
            TerrainChunk *chunk = subsystemTerrain->getTerrainChunk(point.posX, point.posY);
            if (chunk == nullptr)
            {
                subsystemTerrain->loadTerrrainChunk(point.posX, point.posY);
                // Log::Info(fmt::format("需要加载 {},{} 的区块", point.posX, point.posY));
                break;
            }
            else if (chunk->State >= TerrainChunkState::InvalidLight)
            {
                std::vector<TerrainChunk*> chunkVec;
                chunkVec.push_back(chunk);

                std::shared_ptr<SubsystemTerrainPackage> package = std::make_shared<SubsystemTerrainPackage>(chunkVec);
                package->sendType = PackageSendType::TARGET;
                package->client = client;
                GameSingleton::singletonPtr->getSingleton<PackageManager>()->addSendPackageToQueue(package);
                // Log::Info(fmt::format("发送 {},{} 区块", point.posX, point.posY));


                m_requestChunkQueue.pop();
            }
        }


        // chunkSetMutex.lock();
        // while(requestChunkSet.size() > 0)
        // {
        //     if(requestChunkSet.size() >= configure->viewChunkCount * 20)
        //     {
        //         requestChunkSet.clear();
        //         client->disconnect(fmt::format("你的视野范围过大，视野范围必须是 {} 以内", configure->viewChunkCount * 16));
        //         return;
        //     }
        //     Point2 point = *requestChunkSet.begin();
        //     TerrainChunk *chunk = subsystemTerrain->getTerrainChunk(point.posX, point.posY);
        //     if (chunk == nullptr)
        //     {
        //         subsystemTerrain->loadTerrrainChunk(point.posX, point.posY);
        //         // Log::Info(fmt::format("需要加载 {},{} 的区块", point.posX, point.posY));
        //         break;
        //     }
        //     else
        //     {
        //         // Log::Info(fmt::format("{},{} 的区块，已经有了，准备发送给客户端", point.posX, point.posY));

        //         std::shared_ptr<SubsystemTerrainPackage> package = std::make_shared<SubsystemTerrainPackage>(chunk);
        //         package->sendType = PackageSendType::TARGET;
        //         package->client = this->client;
        //         GameSingleton::singletonPtr->getSingleton<PackageManager>()->addSendPackageToQueue(package);
        //         requestChunkSet.erase(requestChunkSet.begin());
        //     }
        // }
        // chunkSetMutex.unlock();
    }

    // 处理玩家名称，防止玩家名字过长，有特殊字符，有敏感词等
    void PlayerData::handleUsername()
    {
        bool needResetName = false;
        if (username.length() > 28 || GameSingleton::messageManager->filterContent(username))
        {
            needResetName = true;
        }
        else
        {
            // 判断玩家名称中是否有非法字符，若有就整个名称都做改变
            for(int32_t index = 0; index < username.length(); index++)
            {
                if(username[index] == ' ' || username[index] == '\n' || username[index] == '\t' || username[index] == '<')
                {
                    needResetName = true;
                    break;
                }
            }
        }
        if (needResetName)
        {
            username = fmt::format("玩家_{:X}", *((uint16_t *)&(playerGuid.guidData.buff[4])));
            client->nickname = username;
        }
    }


    void PlayerData::loadMsgPackJson(const nlohmann::json &jsonData)
    {
        isNewCreate = true;
        

        playerGameMode = PlayerGameMode::Follow;
        gameMode = GameSingleton::singletonPtr->getSingleton<SubsystemGameInfo>()->gameMode;
        
        auto classIt = jsonData.find("PlayerClass");
        if(classIt != jsonData.end())
        {
            playerClass = (*classIt)[1].get<std::string>() == "Male" ? PlayerClass::Male : PlayerClass::Female;
        }

        spawnPosition = Vector3{ GameSingleton::serverConfigure->startSpawnPoint };
        spawnPosition.posX += GameSingleton::gameRandom->getFloat(-(GameSingleton::serverConfigure->startSpawnPointRadius), GameSingleton::serverConfigure->startSpawnPointRadius);
        spawnPosition.posZ += GameSingleton::gameRandom->getFloat(-(GameSingleton::serverConfigure->startSpawnPointRadius), GameSingleton::serverConfigure->startSpawnPointRadius);
        
        if(jsonData.find("CharacterSkinName") != jsonData.end())
        {
            characterSkinName = jsonData["CharacterSkinName"][1].get<std::string>();

            if(GameSingleton::skinsManager->haveSkin(characterSkinName) == false)
            {
                if(GameSingleton::serverConfigure->canCustomSkin == true)
                {
                    GameSingleton::skinsManager->requsetSkin(client, characterSkinName);
                }
                else
                {
                    characterSkinName = (playerClass == PlayerClass::Male ? "$Male1" : "$Female1");
                }
            }
        }

        if(client->nickname != "")
        {
            username = client->nickname;
        }
        else
        {
            auto nameIt = jsonData.find("Name");
            if(nameIt != jsonData.end())
            {
                std::string name = (*nameIt)[1].get<std::string>();
                if(name != "")
                {
                    username = fmt::format("{}_{:X}", name, *((uint16_t *)&(playerGuid.guidData.buff[4])));
                    client->nickname = username;
                }
            }
        }

        handleUsername();

        if(m_machine == nullptr)
        {
            m_machine = new PlayerMachine(this);
        }
    }

    void PlayerData::saveToMsgPackJson(nlohmann::json &jsonData, bool confuse)
    {
        MSG_VALUE_SET(jsonData, "SpawnPosition", "Vector3", spawnPosition.toString());
        MSG_VALUE_SET(jsonData, "FirstSpawnTime", "double", firstSpawnTime);
        MSG_VALUE_SET(jsonData, "LastSpawnTime", "double", lastSpawnTime);
        MSG_VALUE_SET(jsonData, "SpawnsCount", "int", spawnsCount); 
        MSG_VALUE_SET(jsonData, "Name", "string", username);

        if (confuse)
        {
            MSG_VALUE_SET(jsonData, "PlayerGUID", "System.Guid", client->confusionGuid.toString());
        }
        else
        {
            MSG_VALUE_SET(jsonData, "PlayerGUID", "System.Guid", playerGuid.toString());
        }
        
        MSG_VALUE_SET(jsonData, "PlayerClass", "Game.PlayerClass", PlayerClassStr[(uint8_t)playerClass]);
        MSG_VALUE_SET(jsonData, "Level", "float", level);

        // 判断服务器上是否有该皮肤
        if (GameSingleton::skinsManager->haveSkin(characterSkinName))
        {
            MSG_VALUE_SET(jsonData, "CharacterSkinName", "string", characterSkinName);
        }
        else
        {
            MSG_VALUE_SET(jsonData, "CharacterSkinName", "string", "$Male1");
        }


        MSG_VALUE_SET(jsonData, "InputDevice", "Game.WidgetInputDevice", "None");
        MSG_VALUE_SET(jsonData, "ServerManager", "bool", false);
        if(groupKey == GameGuid::GetZeroGuid())
        {
            MSG_VALUE_SET(jsonData, "GroupKey", "string", "");
        }
        else
        {
            MSG_VALUE_SET(jsonData, "GroupKey", "string", groupKey.toString());
        }
    }


    void PlayerData::load(const nlohmann::json &jsonData)
    {
        spawnPosition       = JSON_TRY_GET_VALUE(jsonData, "SpawnPosition", Vector3, FastVector3::Zero);
        firstSpawnTime      = JSON_TRY_GET_VALUE(jsonData, "FirstSpawnTime", int64_t, 0);
        lastSpawnTime       = JSON_TRY_GET_VALUE(jsonData, "LastSpawnTime", int64_t, 0);
        spawnsCount         = JSON_TRY_GET_VALUE(jsonData, "SpawnsCount", int32_t, 0);
        
        if(username == "")
        {
            // 查找 json 数据里面的玩家名称
            username = JSON_TRY_GET_VALUE(jsonData, "Name", std::string, "Walter");
            client->nickname = username;
        }

        playerClass         = (PlayerClass)(JSON_TRY_GET_VALUE(jsonData, "PlayerClass", uint8_t, (uint8_t)PlayerClass::Male));
        level               = JSON_TRY_GET_VALUE(jsonData, "Level", float, 1.0f);
        characterSkinName   = JSON_TRY_GET_VALUE(jsonData, "CharacterSkinName", std::string, "$Male1");
        groupKey            = JSON_TRY_GET_VALUE(jsonData, "GroupKey", GameGuid, GameGuid::GetZeroGuid());

        uint8_t loadGameModeValue = JSON_TRY_GET_VALUE(jsonData, "PlayerGameMode", uint8_t, (uint8_t)PlayerGameMode::Follow);
        playerGameMode      = (PlayerGameMode)loadGameModeValue;

        // 设置玩家的游戏模式
        if(PlayerGameMode::Follow == playerGameMode)
        {
            gameMode = GameSingleton::singletonPtr->getSingleton<SubsystemGameInfo>()->gameMode;
        }
        else
        {
            gameMode = (GameMode)playerGameMode;
        }
        
        // 判断玩家的皮肤在服务器上是否存在，不存在就发一次请求，然后将其先替换成默认皮肤
        if(GameSingleton::skinsManager->haveSkin(characterSkinName) == false)
        {
            if(GameSingleton::serverConfigure->canCustomSkin == true)
            {
                // 向对应的玩家请求皮肤
                GameSingleton::skinsManager->requsetSkin(client, characterSkinName);
                characterSkinName = (playerClass == PlayerClass::Male ? "$Male1" : "$Female1");
            }
            else
            {
                characterSkinName = (playerClass == PlayerClass::Male ? "$Male1" : "$Female1");
            }
        }

        handleUsername();

        if(m_machine == nullptr)
        {
            m_machine = new PlayerMachine(this);
        }
    }

    void PlayerData::save(nlohmann::json &jsonData)
    {
        jsonData["SpawnPosition"] = spawnPosition;
        jsonData["FirstSpawnTime"] = firstSpawnTime;
        jsonData["LastSpawnTime"] = lastSpawnTime;
        jsonData["SpawnsCount"] = spawnsCount;
        jsonData["Name"] = username;
        jsonData["PlayerGUID"] = playerGuid;
        jsonData["PlayerClass"] = (uint8_t)playerClass;
        jsonData["Level"] = level;
        jsonData["CharacterSkinName"] = characterSkinName;
        jsonData["GroupKey"] = groupKey;
        jsonData["PlayerGameMode"] = (uint8_t)playerGameMode;
    }

}
